219 lines
8.1 KiB
TypeScript
219 lines
8.1 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
Form,
|
|
FormControl,
|
|
FormField,
|
|
FormItem,
|
|
FormLabel,
|
|
FormMessage
|
|
} from "@app/components/ui/form";
|
|
import { toast } from "@app/hooks/useToast";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { SetUserRolesResponse } from "@server/routers/user";
|
|
import { AxiosResponse } from "axios";
|
|
import { useEffect, useState } from "react";
|
|
import { useForm } from "react-hook-form";
|
|
import { z } from "zod";
|
|
import { ListRolesResponse } from "@server/routers/role";
|
|
import { userOrgUserContext } from "@app/hooks/useOrgUserContext";
|
|
import { useParams } from "next/navigation";
|
|
import { Button } from "@app/components/ui/button";
|
|
import {
|
|
SettingsContainer,
|
|
SettingsSection,
|
|
SettingsSectionHeader,
|
|
SettingsSectionTitle,
|
|
SettingsSectionDescription,
|
|
SettingsSectionBody,
|
|
SettingsSectionForm,
|
|
SettingsSectionFooter
|
|
} from "@app/components/Settings";
|
|
import { formatAxiosError } from "@app/lib/api";
|
|
import { createApiClient } from "@app/lib/api";
|
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
|
import { Tag, TagInput } from "@app/components/tags/tag-input";
|
|
|
|
const formSchema = z.object({
|
|
username: z.string(),
|
|
roles: z
|
|
.array(
|
|
z.object({
|
|
id: z.string(),
|
|
text: z.string()
|
|
})
|
|
)
|
|
.min(1, { message: "Please select a role" })
|
|
});
|
|
|
|
export default function AccessControlsPage() {
|
|
const { orgUser: user } = userOrgUserContext();
|
|
|
|
const api = createApiClient(useEnvContext());
|
|
|
|
const { orgId } = useParams();
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
const [allRoles, setAllRoles] = useState<{ id: string; text: string }[]>(
|
|
[]
|
|
);
|
|
const [activeRolesTagIndex, setActiveRolesTagIndex] = useState<
|
|
number | null
|
|
>(null);
|
|
|
|
const form = useForm<z.infer<typeof formSchema>>({
|
|
resolver: zodResolver(formSchema),
|
|
defaultValues: {
|
|
username: user.username!,
|
|
roles: []
|
|
}
|
|
});
|
|
|
|
useEffect(() => {
|
|
async function fetchRoles() {
|
|
const res = await api
|
|
.get<AxiosResponse<ListRolesResponse>>(`/org/${orgId}/roles`)
|
|
.catch((e) => {
|
|
console.error(e);
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Failed to fetch roles",
|
|
description: formatAxiosError(
|
|
e,
|
|
"An error occurred while fetching the roles"
|
|
)
|
|
});
|
|
});
|
|
|
|
if (res?.status === 200) {
|
|
setAllRoles(
|
|
res.data.data.roles.map((role) => ({
|
|
id: role.roleId.toString(),
|
|
text: role.name
|
|
}))
|
|
);
|
|
}
|
|
}
|
|
|
|
fetchRoles();
|
|
|
|
form.setValue(
|
|
"roles",
|
|
user.roles.map((i) => ({
|
|
id: i.id.toString(),
|
|
text: i.name
|
|
}))
|
|
);
|
|
}, []);
|
|
|
|
async function onSubmit(values: z.infer<typeof formSchema>) {
|
|
setLoading(true);
|
|
|
|
const res = await api
|
|
.post<
|
|
AxiosResponse<SetUserRolesResponse>
|
|
>(`/org/${user.orgId}/user/${user.userId}/roles`, { roleIds: values.roles.map((r) => parseInt(r.id)) })
|
|
.catch((e) => {
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Failed to add user to role",
|
|
description: formatAxiosError(
|
|
e,
|
|
"An error occurred while adding user to the role."
|
|
)
|
|
});
|
|
});
|
|
|
|
if (res && res.status === 200) {
|
|
toast({
|
|
variant: "default",
|
|
title: "User saved",
|
|
description: "The user has been updated."
|
|
});
|
|
}
|
|
|
|
setLoading(false);
|
|
}
|
|
|
|
return (
|
|
<SettingsContainer>
|
|
<SettingsSection>
|
|
<SettingsSectionHeader>
|
|
<SettingsSectionTitle>Access Controls</SettingsSectionTitle>
|
|
<SettingsSectionDescription>
|
|
Manage what this user can access and do in the
|
|
organization
|
|
</SettingsSectionDescription>
|
|
</SettingsSectionHeader>
|
|
|
|
<SettingsSectionBody>
|
|
<SettingsSectionForm>
|
|
<Form {...form}>
|
|
<form
|
|
onSubmit={form.handleSubmit(onSubmit)}
|
|
className="space-y-4"
|
|
id="access-controls-form"
|
|
>
|
|
<FormField
|
|
control={form.control}
|
|
name="roles"
|
|
render={({ field }) => (
|
|
<FormItem className="flex flex-col items-start">
|
|
<FormLabel>Roles</FormLabel>
|
|
<FormControl>
|
|
<TagInput
|
|
{...field}
|
|
activeTagIndex={
|
|
activeRolesTagIndex
|
|
}
|
|
setActiveTagIndex={
|
|
setActiveRolesTagIndex
|
|
}
|
|
placeholder="Select a role"
|
|
size="sm"
|
|
tags={
|
|
form.getValues().roles
|
|
}
|
|
setTags={(newRoles) => {
|
|
form.setValue(
|
|
"roles",
|
|
newRoles as [
|
|
Tag,
|
|
...Tag[]
|
|
]
|
|
);
|
|
}}
|
|
enableAutocomplete={true}
|
|
autocompleteOptions={
|
|
allRoles
|
|
}
|
|
allowDuplicates={false}
|
|
restrictTagsToAutocompleteOptions={
|
|
true
|
|
}
|
|
sortTags={true}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
</form>
|
|
</Form>
|
|
</SettingsSectionForm>
|
|
</SettingsSectionBody>
|
|
|
|
<SettingsSectionFooter>
|
|
<Button
|
|
type="submit"
|
|
loading={loading}
|
|
disabled={loading}
|
|
form="access-controls-form"
|
|
>
|
|
Save Access Controls
|
|
</Button>
|
|
</SettingsSectionFooter>
|
|
</SettingsSection>
|
|
</SettingsContainer>
|
|
);
|
|
}
|